User ID controlled by request parameter
Escalada horizontal (IDOR) en la página de cuenta: el endpoint usa el parámetro id directamente para resolver el perfil, exponiendo la API key de otros usuarios.
Target Overview & Vulnerability Summary
El lab access-control/lab-user-id-controlled-by-request-parameter tiene una vulnerabilidad de escalada horizontal en la página de cuenta: el perfil se resuelve por el parámetro id de la request sin verificar que pertenezca a la sesión autenticada. Cambiando id al usuario carlos se obtiene su API key.
| Campo | Valor |
|---|---|
| Severidad | Alta (exfiltración de datos de otros usuarios — escalada horizontal) |
| Endpoint vulnerable | /my-account?id=<username> |
| Root cause | Autorización por parámetro de la request en lugar de por identidad de sesión (IDOR / Broken Object Level Authorization) |
Credenciales del laboratorio: wiener:peter.
Technical Breakdown & Exploitation Flow
1. Autenticación y observación
Autenticado como wiener, la página de cuenta se carga vía /my-account?id=wiener. La ruta utiliza el id para resolver qué perfil mostrar, incluida la API key del usuario.
2. Cambio del parámetro id
GET /my-account?id=carlos HTTP/2
Host: <LAB-ID>.web-security-academy.net
Cookie: session=<SESSION_ID>
La respuesta devuelve la página de cuenta de carlos con su API key:
Your username is: carlos
Your API key is: <API_KEY_CARLOS>
Se envía la API key de carlos como solución del lab. El endpoint no compara el id solicitado con el usuario de la sesión.
Root Cause Analysis
La ruta resuelve el recurso a partir del parámetro id de la request y confía en que el usuario autenticado solo pedirá su propio perfil. No hay una comprobación de autorización entre la identidad de la sesión (cookie) y el identificador del recurso solicitado. Es un IDOR clásico: la referencia directa a objeto (id) no está verificada contra el propietario. La decisión de acceso se toma sobre datos del cliente en lugar de sobre la identidad autenticada.
Secure Remediation
- Derivar el identificador del recurso de la sesión, nunca del parámetro, o validar que ambos coincidan:
def my_account(request):
session_user = request.session.user # identidad de la sesión
requested = request.params.get("id", session_user)
if requested != session_user:
abort(403) # autorización por propiedad
return render_profile(requested)
- En APIs: resolver el objeto desde el contexto autenticado (
/me,/accounts/{id}con chequeo de ownership) en lugar de aceptaridarbitrario. - Aplicar pruebas de autorización por recurso (cada
iddebe validarse contra el principal) y no solo de autenticación global.